﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System;
using System.ComponentModel;
using System.Windows.Media;

namespace Xceed.Silverlight.Controls
{
  [TemplateVisualState( GroupName = "LicensingStates", Name = "Licensed" )]
  [TemplateVisualState( GroupName = "LicensingStates", Name = "NotLicensed" )]
  public class AutoSelectTextBox : TextBox, ISupportInitialize
  {
    #region AutoSelectBehavior PROPERTY

    public AutoSelectBehavior AutoSelectBehavior
    {
      get
      {
        return ( AutoSelectBehavior )GetValue( AutoSelectBehaviorProperty );
      }
      set
      {
        SetValue( AutoSelectBehaviorProperty, value );
      }
    }

    public static readonly DependencyProperty AutoSelectBehaviorProperty =
        DependencyProperty.Register( "AutoSelectBehavior", typeof( AutoSelectBehavior ), typeof( AutoSelectTextBox ),
      new PropertyMetadata( AutoSelectBehavior.Never ) );

    #endregion AutoSelectBehavior PROPERTY

    #region Contructor

    public AutoSelectTextBox()
    {


      this.TextChanged += new TextChangedEventHandler( this.OnTextChanged );

      this.Loaded += new RoutedEventHandler( this.OnLoaded );
      this.Unloaded += new RoutedEventHandler( this.OnUnloaded );

#if !SILVERLIGHT

      //The OnKeyDown override is not called for navigation keys in WPF,
      //so we add an handler to make sure keys routed events will be bubbled properly.
      //The PreviewKeyDown to calculate the position in the TextBox
      //And the KeyDown to raise the event at the right moment.
      this.AddHandler( UIElement.PreviewKeyDownEvent, new KeyEventHandler( OnPreviewKeyDown ) );
      this.AddHandler( UIElement.KeyDownEvent, new KeyEventHandler( OnKeyDown ), true );

#endif
    }

    private void OnLoaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualState( false );
    }

    private void OnUnloaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualState( false );
    }

    #endregion

#if !SILVERLIGHT

    #region EdgeKeyDown Event

    //This is necessary in WPF because the arrows KeyDown events are not bubbled to the OnKeyDown override in DataGridControl,
    //thus the navigation does not work when editing a control such as TextBox.
    //The EdgeKeyDownEvent is bubbled when a navigation key is pressed on the edge of the TextBox.
    public static readonly RoutedEvent EdgeKeyDownEvent = EventManager.RegisterRoutedEvent(
      "EdgeKeyDown",
      RoutingStrategy.Bubble,
      typeof( KeyEventHandler ),
      typeof( AutoSelectTextBox ) );

    #endregion

#endif

    protected override void OnKeyDown( KeyEventArgs e )
    {
      base.OnKeyDown( e );

      if( !e.Handled )
      {
        if( ( Keyboard.Modifiers & ModifierKeys.Shift ) == ModifierKeys.Shift )
        {
          switch( e.Key )
          {
            case Key.Up:
            case Key.Left:
              //Shift-Left with selection at beginning.
              //Shift-Up with selection at beginning.
              if( this.SelectionStart == 0 )
              {
                e.Handled = true;
              }
              break;

            case Key.Right:
            case Key.Down:
              //Shift-Right with selection at end.
              //Shift-Down with selection at end.
              if( ( this.SelectionStart + this.SelectionLength ) == this.Text.Length )
              {
                e.Handled = true;
              }
              break;

            default: // do nothing
              break;
          }
        }
      }
    }

    protected override void OnGotFocus( RoutedEventArgs e )
    {
      base.OnGotFocus( e );

      if( this.AutoSelectBehavior == AutoSelectBehavior.OnFocus )
      {
        this.SelectAll();
      }

      if( this.CaretBrush != null )
      {
        SolidColorBrush newBrush = this.CaretBrush as SolidColorBrush;
        if( newBrush != null )
        {
          this.CaretBrush = new SolidColorBrush( ( ( SolidColorBrush )this.CaretBrush ).Color );
        }
      }
    }

    protected virtual void OnTextChanged( object sender, TextChangedEventArgs e )
    {
      BindingExpression bindingExpression = this.GetBindingExpression( TextBox.TextProperty );

      if( bindingExpression != null )
        bindingExpression.UpdateSource();
    }

    protected void ChangeVisualState()
    {
      this.ChangeVisualState( true );
    }

    protected void ChangeVisualState( bool useTransitions )
    {
      this.UpdateLicensingStates( useTransitions );
      this.ChangeVisualStateCore( useTransitions );
    }

    protected virtual void ChangeVisualStateCore( bool useTransitions )
    {
    }

#if !SILVERLIGHT

    private void OnPreviewKeyDown( object sender, KeyEventArgs e )
    {
      //Validate if the caret is on the edge and a navigation key has been pressed.
      if( this.SelectionLength != 0 )
        return;

      if( e.Key == Key.Right )
      {
        if( ( this.CaretIndex < this.Text.Length ) )
          return;
      }
      else if( e.Key == Key.Left )
      {
        if( this.CaretIndex > 0 )
          return;
      }
      else if( !( e.Key == Key.Up || e.Key == Key.Down ) )
      {
        return;
      }

      //If we raise the event directly here, there will be a second KeyDown event that will bubble to the DataGridControl,
      //originating from the following cell, with the result that there will be a double navigation.
      //Thus the need to raise the event in the KeyDown event.
      m_raiseEdgeKeyDownEvent = true;
    }

    private void OnKeyDown( object sender, KeyEventArgs e )
    {
      //If the caret is on the edge, raise the EdgeKeyDownEvent so it bubbles to the DataGridContol, and the navigation key can be handled.
      if( m_raiseEdgeKeyDownEvent )
      {
        m_raiseEdgeKeyDownEvent = false;

        KeyEventArgs newEventArgs = new KeyEventArgs( e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key );
        newEventArgs.RoutedEvent = AutoSelectTextBox.EdgeKeyDownEvent;
        RaiseEvent( newEventArgs );
      }
    }

#endif

    private void UpdateLicensingStates( bool useTransitions )
    {
      if( m_isLicensed )
      {
        VisualStateManager.GoToState( this, "Licensed", useTransitions );
      }
      else
      {
        VisualStateManager.GoToState( this, "NotLicensed", useTransitions );
      }
    }

    #region Private fields

    //licensing
    private bool m_isLicensed = true;

#if !SILVERLIGHT
    private bool m_raiseEdgeKeyDownEvent = false;
#endif

    #endregion

    #region ISupportInitialize Members

    void ISupportInitialize.BeginInit()
    {
      m_iSupportInitializeCount++;

      if( m_iSupportInitializeCount == 1 )
      {
        this.BeginInitCore();
      }
    }

    void ISupportInitialize.EndInit()
    {
      if( m_iSupportInitializeCount == 0 )
        return;

      m_iSupportInitializeCount--;

      if( m_iSupportInitializeCount == 0 )
      {
        this.IsControlInitialized = true;
        this.EndInitCore();
      }
    }

    protected bool IsControlInitialized
    {
      get;
      private set;
    }

    protected virtual void BeginInitCore()
    {
    }

    protected virtual void EndInitCore()
    {
    }

    private int m_iSupportInitializeCount;

    #endregion
  }
}
